﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.IO;
using Studio.Network.Utilities;
using System.Drawing;

namespace Studio.Network.WebEngine
{
    public class HtmlEngine
    {
        WebBrowser webBrowser;
        
        bool done;

        bool changed;

        bool reset;

        public static readonly string ELEMENT_ID_VAR = "var_span_marstone_net";

        public static readonly string ELEMENT_ID_JSON = "json_div_marstone_net";

        public static readonly string FILE_NAME_JSON_PARSER = "json2.js";


        public HtmlEngine()
        {
            webBrowser = new WebBrowser();
            webBrowser.DocumentCompleted += NavigateComplete;
            webBrowser.ProgressChanged += ProgressChanged;
            reset = changed = done = false;
        }

        [STAThread] 
        void NavigateComplete(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            HtmlDocument document = webBrowser.Document;
            cookieContainer.SetCookies(e.Url, document.Cookie);
            done = true;
        }

        private CookieContainer cookieContainer = new CookieContainer();


        [STAThread]
        public HtmlDocument Navigate(Uri uri)
        {
            BeforeNavigate();
            webBrowser.Navigate(uri);
            WaitComplete();

            return webBrowser.Document;
        }

        /// <summary>
        /// Engine can NOT be enter TWICE!
        /// </summary>
        public bool CanEnter
        {
            get 
            {
                return (done && changed);
            }
        }

        public void BeforeNavigate()
        {
            reset = done = changed = false;
        }

        /// <summary>
        /// Wait while navigate complete
        /// </summary>
        [STAThread]
        public void WaitComplete()
        {
            // There will be a risk of an eternal loop
            while (false == done && false == reset)
                Application.DoEvents();
            // set cookie
            cookieContainer.SetCookies(webBrowser.Url, webBrowser.Document.Cookie);
        }

        [STAThread]
        public void WaitChanged()
        {
            // There will be a risk of an eternal loop
            while (false == changed && false == reset)
                Application.DoEvents();
            // set cookie
            cookieContainer.SetCookies(webBrowser.Url, webBrowser.Document.Cookie);
        }

        void ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
        {
            // set changed while current progress reached 100%
            //if (e.MaximumProgress == e.CurrentProgress)
            changed = true;
            // System.Diagnostics.Debug.WriteLine(string.Format("Current={0}, Maximum={1}", e.CurrentProgress, e.MaximumProgress));
        }


        [STAThread]
        public string ReadVar(string varName) 
        {
            HtmlElement varElement = Document.GetElementById(HtmlEngine.ELEMENT_ID_VAR);
            string var = string.Empty;

            // If no such element, we create it.
            if(null == varElement) 
            {
                // Create a temporary span element:
                varElement = Document.CreateElement("span");
                // ensure it is unqiue
                varElement.Id = ELEMENT_ID_VAR;
                // Append to Body
                Document.Body.AppendChild(varElement);
            }
            // Clear
            varElement.InnerHtml = string.Empty;

            this.InjectJSONParser();

            HtmlElement json = Document.GetElementById(HtmlEngine.ELEMENT_ID_JSON);
            // System.Console.WriteLine(json.InnerHtml);

            // Dump the variable contents into the span:
            string url = string.Format("javascript:document.getElementById('{0}').innerHTML=JSON.stringify({1});void(0)", 
                ELEMENT_ID_VAR, varName);

            // string url = "javascript:window.alert(g_verify);void(0)";
            System.Console.WriteLine(url);

            BeforeNavigate();
            webBrowser.Navigate(new Uri(url));
            WaitChanged();

            // Grab it into our app:
            varElement = Document.GetElementById(ELEMENT_ID_VAR);
            var = varElement.InnerHtml;

            System.Diagnostics.Debug.WriteLine(string.Format("{0}={1}", varName, var));
            return var;

            // Delete the temporary element:
            // We dont do it for it is only supported by mshtml.dll
            // varElement.varElement.parentNode.RemoveChild(varElement)
        }

        public void InjectJSONParser()
        {

            HtmlElement jsonElement = Document.GetElementById(HtmlEngine.ELEMENT_ID_JSON);
            
            // If no such element, we create it.
            if (null == jsonElement)
            {
                // Create a temporary span element:
                jsonElement = Document.CreateElement("script");
                // ensure it is unqiue
                jsonElement.Id = ELEMENT_ID_JSON;
                // read json2.js
                string json2 = null;
                FileStream fs = null;
                TextReader sr = null;
                // open file
                string fn = HtmlEngine.FILE_NAME_JSON_PARSER;
                fs = File.Open(fn, FileMode.Open);
                sr = new StreamReader(fs);
                // read to end
                json2 = sr.ReadToEnd();
                jsonElement.SetAttribute("type", "text/javascript");
                jsonElement.SetAttribute("language", "JavaScript");
                // jsonElement.SetAttribute("text", "alert('welcome!')");
                jsonElement.SetAttribute("text", json2);
                // Append to Body
                Document.Body.AppendChild(jsonElement);
            }
        }

        //private void RemoveChild(String ElementId)
        //{
            //HTMLDocumentClass htmlDoc = browser.Document as HTMLDocumentClass;
            //IHTMLDOMNode node = htmlDoc.getElementById("tr2") as IHTMLDOMNode;
            //MessageBox.Show("Click to remove the second row...");
            //node.parentNode.removeChild(node);
        //}

        public CookieContainer CookieContainer
        {
            get
            {
                return cookieContainer;
            }
        }

        public HtmlDocument Document
        {
            get
            {
                return webBrowser.Document;
            }
        }

        WebProxy proxy = null;

        public WebProxy Proxy
        {
            get { return proxy; }
            set { proxy = value; }
        }

        public string AjaxRequest(Uri uri, string referer)
        {
            return this.AjaxRequest(uri, Encoding.Default, Encoding.Default, referer);
        }

        /// <summary>
        /// POST with referer
        /// </summary>
        public string AjaxRequest(Uri uri, Encoding request, Encoding response, string referer)
        {
            HttpHelper helper = new HttpHelper(cookieContainer);
            helper.Proxy = this.proxy;
            helper.ResponseEncoding = response;
            helper.RequestEncoding = request;
            return helper.POST(uri.AbsoluteUri, referer);
        }

        public string GET(string url, Encoding request, Encoding response, string referer)
        {
            HttpHelper helper = new HttpHelper(cookieContainer);
            helper.Proxy = this.proxy;
            helper.ResponseEncoding = response;
            helper.RequestEncoding = request;
            // return helper.AdvancedRequest("www.kaixin001.com", 80, url);
            return helper.GET(url, referer);
        }

        public Image GetImage(Uri uri)
        { 
            HttpHelper helper = new HttpHelper(cookieContainer);
            helper.Proxy = this.proxy;
            return helper.GetImage(uri.AbsoluteUri);
        }

        public string POST(string url, string referer)
        {
            return this.AjaxRequest(new Uri(url), referer);
        }

        public static readonly string ABOUT_BLANK = "about:blank";

        public void Reset()
        {
            reset = true;
            webBrowser.Stop();
            webBrowser.Navigate(new Uri(ABOUT_BLANK));
        }
    }
}
